<#assign object = doc.object>
package ${object.package}.service;

import net.sf.json.JSONObject;

import java.io.PrintWriter;
<#if doc["/object/attributes/date[@finder='true']"][0]??>
import java.util.Date;
</#if>
import java.sql.SQLException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import redora.rdo.exceptions.*;
import redora.rdo.impl.*;
import ${object.package}.model.*;
import ${object.package}.model.fields.${object.name}Fields;
<#list doc["//enum[@scope='global']"] as enum>
import ${object.package}.model.enums.${enum.@class};
</#list>

public class ${object.name}ServiceJSON extends ServiceBase {
	private Logger l = Logger.getLogger("${object.package}.service.${object.name}ServiceJSON");
	
	public ${object.name}ServiceJSON() throws ConnectException {
		super();
	}
	
	/** @return Null, or the desired ${object.name} */
	public void findById(Long id, PrintWriter out) throws ConnectException, QueryException, CopyException {
		try {
			makePreparedStatement(${object.name}SQL.FIND_BY_ID);
			ps.setLong(1, id.longValue());
			rs = ps.executeQuery();
			out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
			if (rs.next()) {
				${object.name}Util.jsonStream(rs, 0, out);
			}
			out.print('}');
			out.print('}');
			rs.close();
			rs = null;
		} catch(SQLException e) {
			l.log(Level.SEVERE, "Failed to run query " + ${object.name}SQL.FIND_BY_ID, e);
			throw new QueryException("Failed to run query: " + ${object.name}SQL.FIND_BY_ID, e);
	  	}
	}

	/**
	* Finds records by custom SQL
	* //TODO add support for parameters
	*/
	public void find(String SQL, PrintWriter out) throws ConnectException, QueryException, CopyException {
		try {
			makePreparedStatement(SQL);
			rs = ps.executeQuery();
			out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
			out.print('[');
			while (rs.next()) {
				if(!rs.isFirst()){
					out.print(',');
				}
				${object.name}Util.jsonStream(rs, 0, out);
			}
			out.print(']');
			out.print('}');
			out.print('}');
			rs.close();
			rs = null;
		} catch(SQLException e) {
			l.log(Level.SEVERE, "Failed to run query: " + SQL, e);
			throw new QueryException("Failed to run query: " + SQL, e);
	  	}
	}

	public void findAll(PrintWriter out) throws ConnectException, QueryException, CopyException {
		try {
			makePreparedStatement(${object.name}SQL.FIND_ALL);
			rs = ps.executeQuery();
			out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
			out.print('[');
			while (rs.next()) {
				if(!rs.isFirst()){
					out.print(',');
				}
				${object.name}Util.jsonStream(rs, 0, out);
			}
			out.print(']');
			out.print('}');
			out.print('}');
			rs.close();
			rs = null;
	  	} catch(SQLException e) {
			l.log(Level.SEVERE, "Failed to run query " + ${object.name}SQL.FIND_ALL, e);
			throw new QueryException("Failed to run query " + ${object.name}SQL.FIND_ALL, e);
	  	}
	}
	
<#list object.attributes?children as att>
    <#if (att?node_name == "long" && att.@name?ends_with("Id")) || att?node_name == "object">
      <@relation att=att />
	public void findBy${relation_name?cap_first}(Long id, PrintWriter out) throws ConnectException, QueryException, CopyException {
		try {
	    	makePreparedStatement(${object.name}SQL.FIND_BY_${relation_name?upper_case});
	    	ps.setLong(1, id.longValue());
	    	rs = ps.executeQuery();
	    	out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
		    out.print('[');
		    while (rs.next()) {
		      if(!rs.isFirst()){
		        out.print(',');
		      }
		      ${object.name}Util.jsonStream(rs, 0, out);
		    }
		    out.print(']');
		    out.print('}');
			out.print('}');
		    rs.close();
		    rs = null;
	  	} catch(SQLException e) {
	    	l.log(Level.SEVERE, "Failed to run query " + ${object.name}SQL.FIND_BY_${relation_name?upper_case}, e);
	    	throw new QueryException("Failed to run query: " + ${object.name}SQL.FIND_BY_${relation_name?upper_case}, e);
	    }
	}
    </#if>
</#list>

<#list object.attributes.enum as att>
	public void findBy${att.@class}(<#if !att.@scope[0]?? || att.@scope == "local">${object.name}.</#if>${att.@class} ${att.@class?uncap_first}, PrintWriter out) throws ConnectException, QueryException, CopyException {
		try {
			makePreparedStatement(${object.name}SQL.FIND_BY_${att.@class?upper_case});
			ps.setString(1, ${att.@class?uncap_first}.name());
			rs = ps.executeQuery();
			out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
			out.print('[');
			while (rs.next()) {
				if(!rs.isFirst()){
					out.print(',');
				}
				${object.name}Util.jsonStream(rs, 0, out);
			}
			out.print(']');
			out.print('}');
			out.print('}');
			rs.close();
			rs = null;
		} catch(SQLException e) {
			l.log(Level.SEVERE, "Failed to run query " + ${object.name}SQL.FIND_BY_${att.@class?upper_case}, e);
			throw new QueryException("Failed to run query: " + ${object.name}SQL.FIND_BY_${att.@class?upper_case}, e);
	  	}
	}
</#list>

<#list doc["/object/attributes/*[@finder='true']"] as att>
	public void findBy${att.@name?cap_first}(${att?node_name?cap_first} finder, PrintWriter out) throws ConnectException, QueryException, CopyException {
		try {
			makePreparedStatement(${object.name}SQL.FIND_BY_${att.@name?upper_case});
			<#if att?node_name == "long">
			ps.setLong(1, finder.longValue());
			<#elseif att?node_name == "integer">
			ps.setInt(1, finder.intValue());
			<#elseif att?node_name == "string">
			ps.setString(1, finder);
			<#elseif att?node_name == "enum">
			ps.setString(1, finder.name());
			<#elseif att?node_name == "date">
			ps.setDate(1, new java.sql.Date(finder.getTime()));
			<#elseif att?node_name == "boolean">
			ps.setBoolean(1, finder.booleanValue());
			</#if>
			rs = ps.executeQuery();
			out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
			out.print('[');
			while (rs.next()) {
				if(!rs.isFirst()){
					out.print(',');
				}
				${object.name}Util.jsonStream(rs, 0, out);
			}
			out.print(']');
			out.print('}');
			out.print('}');
			rs.close();
			rs = null;
		} catch(SQLException e) {
			l.log(Level.SEVERE, "Failed to run query " + ${object.name}SQL.FIND_BY_${att.@name?upper_case}, e);
			throw new QueryException("Failed to run query: " + ${object.name}SQL.FIND_BY_${att.@name?upper_case}, e);
	  	}
	}
</#list>

<#list doc["/object/attributes/string[@lazy='true']"] as att>
	public void find${att.@name?cap_first}ById (Long id, PrintWriter out) throws ConnectException, QueryException {
		try {
			makePreparedStatement(${object.name}SQL.FIND_${att.@name?upper_case}_BY_ID);
			ps.setLong(1,id);
			rs = ps.executeQuery();
			out.print('{');
			out.print("response:");
			out.print('{');
			out.print("status:0,data:");
			if (rs.next())
				out.print(rs.getString(1));
			out.print('}');
			out.print('}');
			rs.close();
			rs = null;
		} catch(SQLException e) {
			l.log(Level.SEVERE, "Failed to run query " + ${object.name}SQL.FIND_${att.@name?upper_case}_BY_ID, e);
			throw new QueryException("Failed to run query: " + ${object.name}SQL.FIND_${att.@name?upper_case}_BY_ID, e);
	  	}
	}
</#list>

	public void findByParams(Map<String,String[]> params, PrintWriter out)
			throws ConnectException, QueryException, CopyException {
			<#list doc["/object/attributes/*[@finder='true']"] as att>
			if (params.containsKey("${att.@name}")) {
				System.out.println(params.get("${att.@name}")[0].toString());
				<#if att?node_name == "long">
				findBy${att.@name?cap_first}(Long.valueOf(params.get("${att.@name}")[0].toString()), out);
				<#elseif att?node_name == "integer">
				findBy${att.@name?cap_first}(Integer.parseInt(params.get("${att.@name}")[0].toString()), out);
				<#elseif att?node_name == "string">
				findBy${att.@name?cap_first}(params.get("${att.@name}")[0].toString(), out);
				<#elseif att?node_name == "date">
				;
				// findBy${att.@name?cap_first}(params.get("${att.@name}")[0].toString(), out);
				<#elseif att?node_name == "boolean">
				findBy${att.@name?cap_first}(Boolean.valueOf(params.get("${att.@name}")[0].toString()), out);
				</#if>
			}
			</#list>
	}

	/**
	 * Create an ${object.name} object from given json string. If the ${object.name} was already
	 * persisted, this ${object.name} is retrieved from persistence before the json string is copied over
	 * the returned object.
	 *  
	 * @param json
	 * @return
	 */
	public ${object.name} fromJson(JSONObject json) throws ConnectException, QueryException, CopyException, LazyException {
		${object.name} retVal = new ${object.name}();
		if (json.has(${object.name}Fields.id.name()) && json.getLong(${object.name}Fields.id.name()) > 0) {
			retVal = ServiceFactory.${object.name?uncap_first}Service().findById(json.getLong(${object.name}Fields.id.name()));
			l.log(Level.INFO, "Retrieved ${object.name} object with id: " + retVal.getId() + " from the database");
		}
		
		${object.name}Util.copy(json, retVal);
		
		return retVal;
	}
}
